#pragma once
#include <unordered_map>
#include <stdint.h>
#include <utility>
#include <cassert>

#include "Socket.hpp"
#include "epoller.hpp"
#include "Connect.hpp"
#include "Protocol.hpp"

const uint16_t defaultport = 8080;
const int defaultMaxevents = 64;
class TcpServer
{
    // 默认函数
public:
    TcpServer(func func, uint16_t port = defaultport) : _service(func), _port(port), _revs(nullptr)
    {
    }

    ~TcpServer()
    {
        _listenSock.Close();
        _epoll.Close();
        if (nullptr == _revs)
            delete[] _revs;
    }

    // 功能函数
public:
    // 链接存在否
    bool IsConnectionExist(int sock)
    {
        auto iter = _sockConn.find(sock);
        return iter != _sockConn.end();
    }

    // tcpSever中的写、读、处理错误的方法
    void Sender(Connection *conn)
    {
        while (true)
        {
            ssize_t s = send(conn->_sock, conn->_outBuffer.c_str(), conn->_outBuffer.size(), 0);
            if (s > 0)
            {
                if (conn->_outBuffer.empty())
                {
                    break;
                }
                else//发送后要清理发送缓存区
                    conn->_outBuffer.erase(0, s);
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    if (conn->_excepter)
                    {
                        conn->_excepter(conn);
                        return;
                    }
                }
            }
        }
        // 如果没有发送完毕，需要对对应的sock开启对写事件的关系， 如果发完了，我们要关闭对写事件的关心！
        if (!conn->_outBuffer.empty())
            conn->_server->EnableReadWrite(conn, true, true);
        else
            conn->_server->EnableReadWrite(conn, true, false);
    }

    void Rcver(Connection *conn)
    {
        char buffer[1024]; // 单次读取长度

        while (true)
        {
            ssize_t size = recv(conn->_sock, buffer, sizeof(buffer), 0);
            buffer[size] = 0;
            conn->_inBuffer += buffer; // 添加入缓存区          

            // 开始业务逻辑
            _service(conn);
        }
    }

    //遇到错误就删除
    void Excepter(Connection *conn)
    {
        logMessage(DEBUG, "Excepter begin");
        _epoll.Control(conn->_sock, 0, EPOLL_CTL_DEL);
        conn->Close();
        _sockConn.erase(conn->_sock);

        logMessage(DEBUG, "关闭%d 文件描述符的所有的资源", conn->_sock);

        delete conn;
    }

    // 监听套接字的accept
    void Accepter(Connection *)
    {
        while (true)
        {
            // 获取到新的套接字
            std::string clientIp;
            uint16_t clientPort;
            int err;
            int newSock = _listenSock.Accept(&clientIp, &clientPort, &err); // accept内部已有确定接收成功的日志

            if (newSock > 0)
            {
                // 将新套接字（链接）添加    (这里只关心读，如要添加写，添加即可EPOLLOUT)
                AddConnect(newSock, EPOLLIN | EPOLLET,
                           std::bind(&TcpServer::Rcver, this, std::placeholders::_1),
                           std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                           std::bind(&TcpServer::Excepter, this, std::placeholders::_1));

                logMessage(DEBUG, "get a new link, info: [%s:%d]", clientIp.c_str(), clientPort);
            }
            else
            {
                if (err == EAGAIN || err == EWOULDBLOCK)
                    break;
                else if (err == EINTR)
                    continue;
                else
                    break;
            }
        }
    }

    //修改链接（套接字）是否可以读/写
    void EnableReadWrite(Connection *conn, bool readable, bool writeable)
    {
        uint32_t event = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET;
        _epoll.Control(conn->_sock, event, EPOLL_CTL_MOD);
    }    
// 需求函数
public:
    // 初始化服务器
    void InitServer()
    {
        // 套接字的初始化
        _listenSock.InitSocket();
        _listenSock.Bind(_port);
        _listenSock.Listen();
        // epoll的初始化

        _epoll.Creat();
        // 将新connect添加到map与epoll中     //读与et模式
        AddConnect(_listenSock.GetSock(), EPOLLIN | EPOLLET,
                   std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
        // 这样传递Accepter函数是由于Addconnect的参数fun的形式

        _revs = new struct epoll_event[defaultMaxevents];
        _maxevents = defaultMaxevents;
    }

    // 添加一个新连接 ——> map + epoll
    void AddConnect(int sock, uint32_t event, func recver, func sender, func excepter)
    {
        // 初始化新链接的信息与套接字的的状态
        Connection *newConn = new Connection(this, sock);
        newConn->Register(recver, sender, excepter);

        // 若是ET模式，要将sock调整为非阻塞
        if (event & EPOLLET)
        {
            Socket tmp(sock);
            tmp.SetNonBlock();
        }

        // 插入map
        _sockConn.insert(std::pair<int, Connection *>(sock, newConn));

        // 加入epoll
        if (!_epoll.AddEvent(sock, event))
        {
            logMessage(ERROR, "epoll add error");
        }

        logMessage(DEBUG, "add new sock : %d in epoll and unordered_map", sock);
    }

    // 单次循环获取就绪事件
    int Loop(int timeout)
    {
        int readNum = _epoll.Wait(_revs, _maxevents, timeout); // 获取已就绪的队列数
        // 遍历就绪队列 -> 将就绪的事件联合_sockConn + _revs，执行
        for (int i = 0; i < readNum; ++i)
        {
            int readySock = _revs[i].data.fd;
            uint16_t readyEvent = _revs[i].events;
            Connection *readyConn = _sockConn[i];

            // 将所有的异常问题，全部转化 成为读写问题
            if (readyEvent & EPOLLERR)
                readyEvent |= (EPOLLIN | EPOLLOUT);
            if (readyEvent & EPOLLHUP) // 该文件描述符不能正常使用，例如：套接字没了、网络断了
                readyEvent |= (EPOLLIN | EPOLLOUT);

            // 执行对应就绪套接字的事件
            // 读事件(监听事件)
            if ((readyEvent & EPOLLIN) && IsConnectionExist(readySock) && readyConn->_recv)
                readyConn->_recv(readyConn);
            // 写事件
            if ((readyEvent & EPOLLOUT) && IsConnectionExist(readySock) && readyConn->_sender)
                readyConn->_sender(readyConn);
        }
        return readNum;
    }

    // 事件派发器
    void Dispatcher()
    {
        int timeout = 1000;
        while (true)
        {
            Loop(timeout);
            // 此处可加以记录 -》每次loop都有可能执行新的就绪事件
        }
    }

private:
    Socket _listenSock;
    uint16_t _port;
    Epoller _epoll;
    struct epoll_event *_revs; // 用于保存已放入epoll的事件
    int _maxevents;
    std::unordered_map<int, Connection *> _sockConn;

    func _service;
};